<?php

namespace ssh;

class SSH2
{
    private $config;
    private $connect;
    private $sftp;

    public function __construct($config)
    {
        $this->config = $config;
        $this->connect = ssh2_connect($config['host'], $config['port']);
        if (!$this->connect) {
            throw new \Exception($config['host'] . "链接失败");
        }
        $rs = ssh2_auth_password($this->connect, $config['user'], $config['password']);
        if (!$rs) {
            throw new \Exception($config['host'] . "链接失败");
        }
        $this->sftp = ssh2_sftp($this->connect);
    }

    public function cmd(...$args)
    {
        $errorLog = "";
        $log = "";
        $fn = fopen("./log/cmd.log", "a+");
        if (!empty($args)) {
            foreach ($args as $index => $arg) {
                fwrite($fn, "[cmd `{$arg}` log]:" . PHP_EOL);
                $stream = ssh2_exec($this->connect, $arg);
                stream_set_blocking($stream, true);
                while ($o = fgets($stream)) {
                    $log .= "$o";
                    fwrite($fn, $o);
                }
                $errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
                if (!empty($errorStream)) {
                    while ($error = fread($errorStream, 1024)) {
                        fwrite($fn, $error);
                        $errorLog .= $error;
                    }
                    if (!empty($errorLog)) {
                        fclose($fn);
                        throw new \Exception($errorLog);
                    }
                }
            }
        }
        fclose($fn);
        return $log;
    }

    public function sendFile($localFile, $remoteFile)
    {
        return ssh2_scp_send($this->connect, $localFile, $remoteFile);
    }

    public function receiveFile($remoteFile, $localFile)
    {
        return ssh2_scp_recv($this->connect, $remoteFile, $localFile);
    }

    public function sendDir($localDir, $remoteDir, $createDirIfEmpty = 1)
    {
        try {
            $this->cmd("ls {$remoteDir}");
        } catch (\Exception $e) {
            try {
                if ($createDirIfEmpty == 1) {
                    $this->cmd("mkdir -p {$remoteDir}");
                } else {
                    return false;
                }
            } catch (\Exception $e) {
                return false;
            }
        }
        $handle = opendir($localDir);
        while ($file = readdir($handle)) {
            if ($file == "." || $file == "..") {
                continue;
            } else {
                $rs = $this->sendFile(rtrim($localDir, "/") . "/" . $file, rtrim($remoteDir, "/") . "/" . $file);
                if (!$rs) {
                    closedir($handle);
                    return false;
                }
            }
        }
        closedir($handle);
        return true;
    }

    public function receiveDir($remoteDir, $localDir)
    {
        try {
            $rs = $this->cmd("ls {$remoteDir}");
            $rs = str_replace(["\r\n", "\r"], "\n", $rs);
            $array = explode("\n", $rs);
            if (file_exists($localDir)) {
                if (!is_dir($localDir)) {
                    return false;
                }
            } else {
                mkdir($localDir, 0755, true);
            }
            foreach ($array as $index => $file) {
                if ($file != "") {
                    $rs = $this->receiveFile(rtrim($remoteDir, "/") . "/" . $file, rtrim($localDir, "/") . "/" . $file);
                    if (!$rs) {
                        return false;
                    }
                }
            }
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function chmod($filename, $mode)
    {
        return ssh2_sftp_chmod($this->sftp, $filename, $mode);
    }

    public function lstat($path)
    {
        return ssh2_sftp_lstat($this->sftp, $path);
    }

    public function mkdir($dirName, $mod = 0755, $recursive = false)
    {
        return ssh2_sftp_mkdir($this->sftp, $dirName, $mod, $recursive);
    }

    public function readlink($link)
    {
        return ssh2_sftp_readlink($$this->sftp, $link);
    }

    public function realpath($filename)
    {
        return ssh2_sftp_realpath($$this->sftp, $filename);
    }

    public function rename($from, $to)
    {
        return ssh2_sftp_rename($this->sftp, $from, $to);
    }

    public function rmdir($dirname)
    {
        return ssh2_sftp_rmdir($this->sftp, $dirname);
    }

    public function stat($path)
    {
        return ssh2_sftp_stat($this->sftp, $path);
    }

    public function symlink($target, $link)
    {
        return ssh2_sftp_symlink($this->sftp, $target, $link);
    }

    public function unlink($filename)
    {
        return ssh2_sftp_unlink($this->connect, $filename);
    }

    public function callback($cb)
    {
        if (is_callable($cb)) {
            call_user_func($cb, $this->connect, $this->sftp);
        } else {
            throw new \Exception('$cb is not callable function');
        }
    }
}